package com.ltedu.gateway.component;

import cn.hutool.core.convert.Convert;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import com.ltedu.common.contants.AuthConstants;
import com.ltedu.common.dto.UserDTO;
import com.ltedu.common.redis.RedisTool;
import com.ltedu.gateway.config.IgnoreUrlsConfig;
import com.nimbusds.jose.JWSObject;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.server.authorization.AuthorizationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.text.ParseException;
import java.util.*;

/**
 * 鉴权管理器
 */
@Component
public class AuthorizationManager implements ReactiveAuthorizationManager<AuthorizationContext> {

    private Logger logger = LoggerFactory.getLogger(AuthorizationManager.class);

    @Autowired
    private IgnoreUrlsConfig ignoreUrlsConfig;

    @Autowired
    private RedisTool redisTool;

    @Override
    public Mono<AuthorizationDecision> check(Mono<Authentication> mono, AuthorizationContext authorizationContext) {
        ServerHttpRequest request = authorizationContext.getExchange().getRequest();
        String path = request.getURI().getPath();
        URI uri = request.getURI();
        PathMatcher pathMatcher = new AntPathMatcher();
        // 1. 白名单路径直接放行
        List<String> ignoreUrls = ignoreUrlsConfig.getUrls();
        for (String ignoreUrl : ignoreUrls) {
            if (pathMatcher.match(ignoreUrl, uri.getPath())) {
                return Mono.just(new AuthorizationDecision(true));
            }
        }
        // 2. 对应跨域的预检请求直接放行
        if (request.getMethod() == HttpMethod.OPTIONS) {
            return Mono.just(new AuthorizationDecision(true));
        }

        List<String> authorities = new ArrayList<>();
        try {
            // 3. token为空拒绝访问
            String token = request.getHeaders().getFirst(AuthConstants.AUTHORIZATION);
            if (StringUtils.isBlank(token)) {
                return Mono.just(new AuthorizationDecision(false));
            }

            String realToken = token.replace(AuthConstants.JWT_TOKEN_PREFIX, "");

            JWSObject jwsObject = JWSObject.parse(realToken);
            String userStr = jwsObject.getPayload().toString();
            JSONObject jsonObject = JSONObject.parseObject(userStr);
            String username = jsonObject.getString("user_name");
            Set<String> permissions = (Set<String>) redisTool.get(username);
            authorities.addAll(permissions);
        } catch (ParseException e) {
            e.printStackTrace();
        }

        // 4. 缓存取权限列表
        Mono<AuthorizationDecision> authorizationDecisionMono = mono
                .filter(Authentication::isAuthenticated)
                .flatMapIterable(Authentication::getAuthorities)
                .map(GrantedAuthority::getAuthority)
                .any(roleId -> {
                    // 5. roleId是请求用户的角色(格式:ROLE_{roleId})，authorities是请求资源所需要角色的集合
                    logger.info("访问路径：{}", path);
                    logger.info("用户角色roleId：{}", roleId);
                    logger.info("资源需要权限authorities：{}", authorities);
                    return authorities.contains(roleId);
                })
                .map(AuthorizationDecision::new)
                .defaultIfEmpty(new AuthorizationDecision(false));
        return authorizationDecisionMono;
    }
}
